{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 问题：Python 里面如何拷贝一个对象？（赋值，浅拷贝，深拷贝的区别）\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面试官想从这道题中考察面试人什么？\n",
    "在很多项目中，由于使用错了拷贝对象的方法，导致一些意想不到的 bug，尤其是在拷贝可变对象的时候，而且像这一类的 bug 并不是很容易发现。所以，正确知道 Python 拷贝对象的三种方式的区别可以减少这一类 bug 的出现。\n",
    "\n",
    "答案\n",
    "直接赋值：其实就是对象的引用（别名）。\n",
    "\n",
    "浅拷贝 (shallow copy)：拷贝父对象，不会拷贝对象的内部的子对象，子对象仍只是使用引用而已；\n",
    "\n",
    "深拷贝 (deep copy)：完全拷贝了父对象及其子对象，不仅拷贝了对象，同时也拷贝了对象中的元素，获得了全新的对象，与被拷贝对象完全独立，但这需要牺牲一定的时间和空间。\n",
    "\n",
    "拓展\n",
    "先看看一个最简单的直接拷贝：\n",
    "\n",
    ">>> a = [1,2]\n",
    ">>> b = a\n",
    ">>> a.append(3)\n",
    ">>> a\n",
    "[1, 2, 3]\n",
    ">>> b\n",
    "[1, 2, 3]\n",
    "b 对 a 只是一个引用而已。我们先了解一下 Python 的赋值语句，我们可以从语句的右边开始了解，Python 先是创建一个列表对象，然后把左边的变量绑定到右边的对象上。也就是说，a 指向一个对象，把 a 拷贝给 b 的时候，也只是告诉 b 去指向一个列表对象。当 a 去做添加元素的操作时，是作用于那个对象，那么 b 和 a 所指向的是同一个对象，所以，a 和 b 的值同时发生了变化。这个在我们的第一小节中就讲过引用的概念了。\n",
    "\n",
    "下面，我们如果用 copy.copy 去浅拷贝 a，看看有什么区别？\n",
    "\n",
    ">>> import copy\n",
    ">>> c = copy.copy(a)\n",
    ">>> a.append(4)\n",
    ">>> a\n",
    "[1, 2, 3, 4]\n",
    ">>> c\n",
    "[1, 2, 3]\n",
    "c 没有像 b 那样，随着 a 的值变化而变化，这是因为，copy.copy 只复制了容器的最外层，这也是它的名字由来。我们也可以通过 id() 函数去查看它们所指的对象的内存位置，我们发现 a 和 b 的内存位置是一样的，c 则不同。这说明 c 另外开了一块内存去存放数据。\n",
    "\n",
    "另外，我们要知道浅拷贝有 3 种形式：切片操作，工厂函数，以及上面的 copy 模块中的 copy 函数。\n",
    "\n",
    "深拷贝拷贝了对象的所有元素。虽然深拷贝可以减少一些不可预知的情况发生，但是这是在牺牲了时间和空间的前提下实现的。\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}